/* 简介：cothread 是一个轻量级协程调度器，由纯C语言实现，易于移植到各种单片机。
 * 同时，由于该调度器仅仅运行在一个实际线程中，所以它也适用于服务器高并发场景。
 *
 * 版本: 1.0.0   2019/02/25
 *
 * 作者: 覃攀 <qinpan1003@qq.com>
 *
 */

#include "rtos.h"

#define SHELL_INPUT_ARRIVE_EVENT (1)

static ccb_t *ccb_shell = NULL;
static int inpos = 0;
static char in_buf[SHELL_BUFFER_SIZE];
static int argc;
static char *argv[10];

static void input_analyse(void)
{
    char *s = in_buf;
    argc = 0;
    
    while (1)
    {
        /* 越过空格 */
        while (*s == ' ') 
            s++;
        if (*s == 0) 
            break;
        
        argv[argc++] = s;

        /* 越过非空格 */
        while(*s != ' ' && *s != 0) 
            s++;
        if(*s == 0) 
            break;
        *s++  = 0;
    }
}

static int do_command(void)
{
    int i;
    
    if (argc == 0)
        return -1;
    
    for (i = 0; i < command_nr; i++)
    {
        if (strcmp(command_table[i].name, argv[0]))
            continue;
        
        return command_table[i].fun(argc,argv);
    }
    
    LOG("\nCommand '%s' not found.\n", argv[0]);
    return -1;
}

/* 唤醒shell线程，通常是串口发送完成中断里调用 */
void wakeup_shell_thread(void)
{
    if (ccb_shell != NULL)
        thread_signal(ccb_shell, OsEvent(SHELL_INPUT_ARRIVE_EVENT));
}

static coresult_t shell_thread(ccb_t *ccb)
{
    char ch;
    
    thread_start();
    
    LOG("\n\n\nWelcome to CoShell.enjoy it!");
    LOG("\nCoShell #");

    while(1)
    {
        thread_wait(OsEvent(SHELL_INPUT_ARRIVE_EVENT), 0);
        OsEventClr(SHELL_INPUT_ARRIVE_EVENT);

        while (read_flag())
        {
            ch = read_data();
            
            if (ch == '\t') ch = ' ';
            if (ch == '\r') ch = '\n';

            if (ch == '\b' && inpos > 0)
            {
                inpos--;
                LOG("%c", '\b');
                continue;
            }

            LOG("%c", ch);
        
            if (ch == '\n' || inpos > SHELL_BUFFER_SIZE - 2)
                break;
            else
                in_buf[inpos++] = ch;
        }

        if (ch != '\n' && inpos <= SHELL_BUFFER_SIZE - 2)
            continue;

        in_buf[inpos] = 0;
        inpos = 0;

        /* 解析输入 */
        input_analyse();

        /* 调用命令 */
        do_command();

        LOG("CoShell #");
    }
    
    thread_end();
}

void create_shell_thread(void)
{
    ccb_shell = thread_create(shell_thread, NULL, THREAD_PRIO_HIGH);
}

